"""
Case Type   : 端口故障类
Case Name   : probackup-restore增量恢复时，主节点TCP端口占用(恢复时主节点会被停掉，可测)
Create At   : 2021/07/13
Owner       : peilinqian
Description :
    1.初始化备份目录
    gs_probackup init -B {backup_dir}
    2.添加一个新的备份实例
    gs_probackup add-instance -B {backup_dir} -D {cluster/dn1}
    --instance={instance_name}
    3.创建指定实例的备份 并查询backup_id
    gs_probackup backup -B {backup_dir}  --instance={instance_name}
    -b full -d postgres -p {port}
    4.stop主机，起线程给主机注入TCP端口被占用故障
    ./cfe "inject rPort_conflict (port) values({port})"
    5.起线程从指定实例的备份中恢复数据
    gs_ctl stop -D {dn1}
    gs_probackup restore -B {backup_dir} --instance {instance_name}
    -D {cluster/dn1} -i {backup_id}
    6.清除故障 恢复环境
    ./cfe "clean rPort_conflict where port ={port}"
    gs_om -t restart
    gs_om -t status --detail
    7.再次从指定实例的备份中恢复 并已backup_dir启动主节点
    gs_probackup restore -B {backup_dir} --instance {instance_name}
    -D {cluster/dn1} -i {backup_id}
    gs_ctl start -D {backup_id}
    8.运行TPCC
    9.检查数据一致性
Expect      :
    1.成功
    2.成功
    3.成功
    4.注入故障成功
    5.恢复数据成功
    6.清除故障 恢复环境
    ./cfe "clean rPort_conflict where port ={port}"
    7.成功 并指定备份路径重启成功
    8.成功
    9.数据一致
History     :
    Modified by 5321170 2021/8/4 : 优化故障清理断言和teardown
    Modified by 5321170 2021/9/27 : 增加备份超时时间
    Modified by peilinqian 2023/10/11 : 增加备份启动库start后等待时间，如果备份库stop失败，不删除备份库路径
"""
import os
import time
import unittest

from testcase.utils.ComThread import ComThread
from testcase.utils.Common import Common
from testcase.utils.CommonSH import CommonSH
from testcase.utils.CommonSH import RestartDbCluster
from testcase.utils.Constant import Constant
from testcase.utils.Logger import Logger
from yat.test import Node, macro


class ReliabilityPort(unittest.TestCase):
    nodes = ("PrimaryDbUser", "Standby1DbUser", "Standby2DbUser")

    @RestartDbCluster(*nodes)
    def setUp(self):
        self.log = Logger()
        self.primary_sh = CommonSH(self.nodes[0])
        self.primary_node = Node(self.nodes[0])

        self.constant = Constant()
        self.com = Common()
        self.primary_root_node = Node(node="PrimaryRoot")
        self.log.info("setUp-实例化:ok")

        self.log.info("Opengauss_Reliability_Port_Case012:开始执行")
        self.log.info("创建备份文件夹")
        self.backup_path = os.path.join(os.path.join(
            os.path.dirname(macro.DB_INSTANCE_PATH)),
            "probackup_dir")
        result = self.com.get_sh_result(self.primary_node,
                                        f"rm -rf {self.backup_path},"
                                        f"mkdir {self.backup_path}")
        self.log.info(result)
        self.log.info("创建恢复用文件夹")
        self.dn_path = os.path.join(macro.DB_INSTANCE_PATH, "tempdn1")
        result = self.com.get_sh_result(self.primary_node,
                                        f"rm -rf {self.dn_path},"
                                        f"mkdir {self.dn_path}")
        self.log.info(result)
        self.assertNotIn("bash", result)

        self.port = Node("PrimaryDbUser").db_port
        self.db_name = Node("PrimaryDbUser").db_name
        self.inject_cmd = f"rPort_conflict (port) values({self.port})"
        self.clean_cmd = f"rPort_conflict where port ={self.port}"
        self.stop_result = False

    def test_main(self):
        try:
            self.log.info("步骤1：初始化备份目录")
            result = self.com.get_sh_result(self.primary_node,
                                            f"source {macro.DB_ENV_PATH};"
                                            f"gs_probackup init "
                                            f"-B {self.backup_path}")
            self.log.info(result)
            self.assertIn("successfully inited", result)
            self.log.info("步骤2：添加一个新的备份实例")
            result = self.com.get_sh_result(self.primary_node,
                                            f"source {macro.DB_ENV_PATH};"
                                            f"gs_probackup add-instance "
                                            f"-B  {self.backup_path} "
                                            f"-D {macro.DB_INSTANCE_PATH} "
                                            f"--instance=pro1")
            self.assertIn("'pro1' successfully inited", result)
            self.log.info("步骤3：创建指定实例的备份")
            result = self.com.get_sh_result(self.primary_node,
                                            f"source {macro.DB_ENV_PATH};"
                                            f"gs_probackup backup "
                                            f"-B {self.backup_path}  "
                                            f"--instance=pro1 -b full "
                                            f"-d {self.db_name} "
                                            f"-p {self.port} --ttl=5 "
                                            f"--archive-timeout=1800")
            self.assertIn('completed', result)
            self.log.info("查询buckup_id")
            backup_idpath = os.path.join(self.backup_path, "backups/pro1")
            result = self.com.get_sh_result(self.primary_node,
                                            f"ls {backup_idpath}")
            backup_id = result.split()[1] \
                if result.split()[0] == "pg_probackup.conf" \
                else result.split()[0]
            self.log.info(backup_id)

            self.log.info("步骤4:stop主机，起线程给主机注入TCP端口被占用故障")
            self.log.info("停止主节点")
            result = self.primary_sh.executeGsctl("stop", "server stopped")
            self.assertTrue(result)
            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertNotIn("gaussdb", result)

            self.log.info("注入故障")
            session1 = ComThread(self.com.cfe_inject,
                                 args=(self.primary_root_node,
                                       self.inject_cmd))
            session1.setDaemon(True)
            session1.start()
            time.sleep(2)

            self.log.info("步骤5：起线程从指定实例的备份中恢复数据")
            self.log.info("从指定实例的备份中恢复数据")
            session2 = ComThread(self.com.get_sh_result,
                                 args=(self.primary_node,
                                       f"source {macro.DB_ENV_PATH};"
                                       f"gs_probackup restore "
                                       f"-B {self.backup_path} "
                                       f"--instance pro1 "
                                       f"-D {self.dn_path} "
                                       f"-i {backup_id}"))
            session2.setDaemon(True)
            session2.start()
            time.sleep(2)

            session1.join(120)
            result = session1.get_result()
            self.log.info(result)

            session2.join(60*10)
            result = session2.get_result()
            self.log.info(result)
            self.assertIn('completed', result)

            self.log.info("注入故障后查看当前端口被占用")
            result = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertIn(self.port, result)
            self.assertIn("0.0.0.0", result)
            self.assertIn("/python", result)
            self.assertNotIn("gaussdb", result)

        finally:
            self.log.info("步骤6:清除故障 恢复环境")
            result = self.com.cfe_clean(self.primary_root_node,
                                        self.clean_cmd)
            self.log.info(result)
            self.assertIn("clean rPortConflict successfully", result)

            self.log.info("查看当前端口未被占用")
            shell_cmd = f"netstat -anp|grep {self.port}"
            self.log.info(shell_cmd)
            result = self.primary_root_node.sh(shell_cmd).result()
            self.log.info(result)
            self.assertEqual("", result.strip())

            self.log.info(f"步骤7：指定路径{self.dn_path}启动主机：成功")
            sql = f"source {macro.DB_ENV_PATH};" \
                f"gs_ctl start -D {self.dn_path}"
            result = self.com.get_sh_result(self.primary_node, sql)
            self.assertIn("server started", result)
            time.sleep(5)

            sql = f"source {macro.DB_ENV_PATH};" \
                f"gs_ctl stop -D {self.dn_path}"
            result = self.com.get_sh_result(self.primary_node, sql)
            self.stop_result = "server stopped" in result
            self.assertIn("server stopped", result)

            self.log.info("重启数据库 恢复为原有dn1路径")
            result = self.primary_sh.stopDbCluster()
            self.log.info(result)
            result = self.primary_sh.startDbCluster()
            self.log.info(result)
            db_status = self.primary_sh.getDbClusterStatus()
            self.log.info(db_status)
            self.assertTrue("Degraded" in db_status or "Normal" in db_status)

        self.log.info("步骤8:运行TPCC")
        result = self.com.startTPCC(self.primary_root_node, macro.TPCC_PATH)
        self.log.info(result)
        self.assertIn(self.constant.TPCC_SUCCESS_MSG, result)

        self.log.info('检查主备是否同步')
        time.sleep(10)
        self.log.info('获取节点数量')
        node_num = self.primary_sh.get_node_num()
        res = self.primary_sh.check_location_consistency("primary", node_num)
        self.assertTrue(res)

        self.log.info('步骤6:检查数据一致性')
        nodes = (self.primary_node, Node(self.nodes[1]), Node(self.nodes[2]))
        flag = self.com.check_data_sample_by_all(r'\d', *nodes)
        self.assertTrue(flag)
        table_dict = self.com.format_sql_result(
            self.primary_sh.executDbSql(r'\d'))
        table_name = table_dict.get('Name')
        for name in table_name:
            select_sql = f'select count(*) from {name};'
            flag = self.com.check_data_sample_by_all(select_sql,
                                                     *nodes)
            self.assertTrue(flag)

    def tearDown(self):
        self.log.info("环境清理")
        self.com.cfe_clean(self.primary_root_node, self.clean_cmd)
        if self.stop_result:
            self.log.info("----备份dn路径stop成功，删除备份路径----")
            self.com.get_sh_result(
                self.primary_node,
                f"rm -rf {self.backup_path} {self.dn_path}")
        else:
            self.log.info("----备份dn路径stop失败，再次stop----")
            time.sleep(5)
            cmd = f"source {macro.DB_ENV_PATH};" \
                f"gs_ctl stop -D {self.dn_path}"
            result = self.com.get_sh_result(self.primary_node, cmd)
            self.log.info(result)
            if "server stopped" in result:
                self.com.get_sh_result(
                    self.primary_node,
                    f"rm -rf {self.backup_path} {self.dn_path}")

        result = self.primary_sh.stopDbCluster()
        self.log.info(result)
        result = self.primary_sh.startDbCluster()
        self.log.info(result)

        db_status = self.primary_sh.getDbClusterStatus("detail")
        self.log.info(db_status)
        self.assertTrue("Degraded" in db_status or "Normal" in db_status)
        self.log.info("Opengauss_Reliability_Port_Case012:执行完成")
